package com.gitee.hermer.boot.jee.service.impl;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.gitee.hermer.boot.jee.commons.exception.ErrorCode;
import com.gitee.hermer.boot.jee.commons.log.UtilsContext;
import com.gitee.hermer.boot.jee.commons.number.IntegerUtils;
import com.gitee.hermer.boot.jee.commons.reflect.ClassUtils;
import com.gitee.hermer.boot.jee.commons.reflect.ReflectUtils;
import com.gitee.hermer.boot.jee.commons.utils.StringUtils;
import com.gitee.hermer.boot.jee.commons.verify.Assert;
import com.gitee.hermer.boot.jee.orm.IBaseDao;
import com.gitee.hermer.boot.jee.orm.annotation.Id;
import com.gitee.hermer.boot.jee.orm.annotation.Version;
import com.gitee.hermer.boot.jee.orm.ibatis.locker.OptimisticLockerException;
import com.gitee.hermer.boot.jee.orm.properties.OrmLockProperties;
import com.gitee.hermer.boot.jee.service.IBaseService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;


public abstract class BaseServiceImpl<T, ID extends Serializable,Dao extends IBaseDao<T, ID>> extends UtilsContext
implements IBaseService<T, ID> {


	protected final Integer pageSize = 10;
	protected final Integer pageNum = 1;

	@Autowired
	private OrmLockProperties lockProperties;


	protected ID getId(T t) throws Throwable{
		Field field = ClassUtils.getField(t.getClass(), Id.class);
		try {
			return (ID) ReflectUtils.invoke(t, null, ClassUtils.getDeclaredMethod(t, StringUtils.genGetter(field.getName())));
		} catch (Throwable e) {
			throw new Throwable(e);
		}
	}

	@Override
	public Integer saveOrUpdate(T t) throws Throwable {
		ID id = getId(t);
		if(id == null || find(id) == null)
			return insert(t);
		else{
			return update(t);
		}
	}


	@Override
	public T find(ID id) throws Throwable {
		Assert.notNull(id);
		Class clazz = ClassUtils.getSuperClassActualTypeArguments(getClass(), 0);
		T t = (T) ClassUtils.newInstance(clazz);
		Field field = ClassUtils.getField(clazz, Id.class);
		Assert.notNullCode(field, ErrorCode.SYSTEM_ERROR, StringUtils.format("Class:{}|未找到{}注解信息！", clazz,Id.class));
		try {
			ReflectUtils.invoke(t, new Object[]{id}, ClassUtils.getDeclaredMethod(t, StringUtils.genSetter(field.getName()), id));
			Assert.notNull(ReflectUtils.invoke(t, null, ClassUtils.getDeclaredMethod(t, StringUtils.genGetter(field.getName()))));
		} catch (Throwable e) {
			throw new Throwable(e);
		}
		return find(t);
	}

	private boolean isEnableOptimisticLocker() {
		try{
			Class clazz = ClassUtils.getSuperClassActualTypeArguments(getClass(), 0);
			return Boolean.valueOf(StringUtils.defaultIfEmpty(getDictValueString("com.boot.jee.orm.optimistic.lock.enable"), "false"))
					&& ClassUtils.hasMethodAnnotation(clazz, Version.class,StringUtils.genGetter(lockProperties.getVersionField()));
		}catch (Throwable e) {
			return false;
		}
	}


	@Autowired
	private Dao ibatisDao;

	protected Dao getIbatisDao(){
		return ibatisDao;
	}

	@Override
	public List<T> list(T t) throws Throwable{
		return ibatisDao.list(t);
	}
	@Override
	public List<T> list() throws Throwable{
		return list(null);
	}

	@Override
	@Transactional(rollbackFor = {Throwable.class})
	public Integer insert(T t) throws Throwable{
		Assert.notNull(t);
		return ibatisDao.insert(t);
	}

	@Override
	public T find(T t) throws Throwable{
		Assert.notNull(t);
		return ibatisDao.find(t);
	}

	@Override
	@Transactional(rollbackFor = {Throwable.class})
	public Integer update(T t) throws Throwable{
		Assert.notNull(t);
		Integer row = 0;
		if((row = ibatisDao.update(t)) == 0 &&isEnableOptimisticLocker() )
			throw new OptimisticLockerException(ErrorCode.SYSTEM_ERROR, new String[]{"Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect): ["+t.getClass()+"#"+
					getId(t)+"]"});
		return row;
	}

	@Override
	@Transactional(rollbackFor = {Throwable.class})
	public Integer delete(T t) throws Throwable{
		Assert.notNull(t);
		return ibatisDao.delete(t);
	}

	@Override
	@Transactional(rollbackFor = {Throwable.class})
	public Integer insertBatch(List<T> list) throws Throwable{
		Assert.notEmpty(list);
		return ibatisDao.insertBatch(list);
	}

	@Override
	@Transactional(rollbackFor = {Throwable.class})
	public Integer updateBatch(List<T> list) throws Throwable{
		Assert.notEmpty(list);
		return ibatisDao.updateBatch(list);

	}

	@Override
	@Transactional(rollbackFor = {Throwable.class})
	public Integer deleteBatch(List<T> list) throws Throwable{
		Assert.notEmpty(list);
		return ibatisDao.deleteBatch(list);
	}

	@Override
	public PageInfo<T> pageList(T t, Integer pageNum, Integer pageSize) throws Throwable {
		if(!IntegerUtils.isBiggerThan0(pageSize))
			pageSize = this.pageSize;
		if (!IntegerUtils.isBiggerThan0(pageNum))
			pageNum = this.pageNum;
		PageHelper.startPage(pageNum, pageSize);
		return new PageInfo<T>(list(t));
	}

}
